package fbp
{
	/*
	Copyright (c) 2008 Justin C. Bozonier

	Permission is hereby granted, free of charge, to any person
	obtaining a copy of this software and associated documentation
	files (the "Software"), to deal in the Software without
	restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the
	Software is furnished to do so, subject to the following
	conditions:
	
	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.
	
	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
	OTHER DEALINGS IN THE SOFTWARE.
	*/
	import flash.events.Event;
	
	import mx.managers.ISystemManager;
	
	public class Network
	{
		var _system_manager:ISystemManager;
		var _components = {};
		var _connections = {};
		
		public function Network(system_manager:ISystemManager)
		{
			if(system_manager == null)
			{
				throw new Error("The system manager can not be null.");
			}
			this._system_manager = system_manager;
		}
		
		public function add_component(name:String, component:NetworkComponent)
		{	
			if(name in this._components)
			{
				throw new Error("The component " + name + " has already been added to the network.");
			}
			
			if(name == null || name == "")
			{
				throw new Error("Invalid network component name.");
			}
			
			if(!(name in this._components) && name != null && name != "")
			{
				this._components[name] = component;
			} 
		}
		
		public function connect(from_component:String, to_component:String)
		{
			if(!(from_component in this._components) || 
				from_component == null || 
				from_component == "" || 
				to_component == "" || 
				to_component == null)
			{
				throw new Error("Invalid network component name.");
			}
			
			if(!(from_component in this._connections))
			{
				this._connections[from_component] = new Array();
			}
			this._connections[from_component].push(to_component);
		}
		
		var _currently_processing = false;
		public function start_processing(useless:*)
		{	
			if(!this._currently_processing)
			{
				this._currently_processing = true;
				
				//Start all of the components in separate threads.
				for(var component_name:String in this._components)
				{
					var thread:PseudoThread = new PseudoThread(this._system_manager, process, component_name);
					thread.addEventListener("threadComplete", threadCompleteHandler);
				}
				
				this._currently_processing = false;
			}
		}
		
		public function threadCompleteHandler(event:Event)
		{
			var thread:PseudoThread = new PseudoThread(this._system_manager, start_processing, null);
			thread.addEventListener("threadComplete", threadCompleteHandler);
		}
		
		public function process(component_name:String)
		{
			//Start all of the components in separate threads
			//Go through the different components
			//If there's data that's been processed
			//Pass the data to the component's connections.
			var component:NetworkComponent = this._components[component_name];
			
			//Pass all processed data along to the appropiate components.
			//I shouldn't be accessing the private var
			//find a way to do this that's more elegant.
			if(component != null && component.has_processed_data())
			{
				component.start_processing();
				
				while(component.has_processed_data())
				{
					//pop each piece of data 
					var processed_data = component.get_data();
					//for every connection spawn a new thread and pass the data into that thread
					//then in the new thread call start_process.
					for each(var connected_component_name in this._connections[component_name])
					{
						var connected_component:NetworkComponent = this._components[connected_component_name];
						connected_component.add_data(processed_data);
					}
				}
			}
		}
	}
}